/*
 * Created on Feb 4, 2005
 * Created by dfhuynh
 */
package edu.mit.simile.longwell;

import java.io.File;
import java.io.IOException;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.PropertyResourceBundle;
import java.util.Set;

//import org.apache.log4j.Logger;

import com.hp.hpl.jena.db.IDBConnection;
import com.hp.hpl.jena.rdf.model.Model;
import com.hp.hpl.jena.rdf.model.ModelFactory;
import com.hp.hpl.jena.rdf.model.ModelMaker;
import com.hp.hpl.jena.rdf.model.Resource;
import com.hp.hpl.jena.rdf.model.Statement;
import com.hp.hpl.jena.rdf.model.StmtIterator;
import com.hp.hpl.jena.sdb.SDBFactory;
import com.hp.hpl.jena.sdb.Store;
import com.hp.hpl.jena.sdb.store.StoreFactory;
import com.hp.hpl.jena.vocabulary.RDF;

import edu.mit.simile.longwell.corpus.Corpus;
import edu.mit.simile.longwell.schema.ISchemaModel;
import edu.mit.simile.longwell.schema.impl.LocalSchemaModel;

/**
 * @author dfhuynh
 */
public abstract class Profile {
	//final static private Logger s_logger =
		//Logger.getLogger(Profile.class);

	final protected String	m_profileID;
	private String username;
	protected Corpus		m_corpus;
	protected Model			m_inferredLayerModel;
	protected Model			m_inferredModel;
	protected Set 			m_structuredModels = new HashSet();
	private HashMap<String, LocalSchemaModel> schemaModels = new HashMap<String, LocalSchemaModel>();
	private PropertyResourceBundle m_languageBundle;
	protected Profile(String profileID, Corpus corpus,PropertyResourceBundle languageBundle) {
		m_profileID = profileID;
		m_corpus = corpus;
		m_languageBundle = languageBundle;
		// You need to create the various models in your subclass
	}
	
	/**
	 * Dispose this profile and free up resources.
	 */
	synchronized public void dispose() {
		disposeStructuredModels();

		m_corpus.dispose();
		m_corpus = null;
	}
	public PropertyResourceBundle getLanguageBundle(){
		return m_languageBundle;
	}
	public String getID() {
		return m_profileID;
	}
	public void setUsername(String user){
		if(user==null){
			username=LongwellServer.getServer().getUserName(m_profileID);
		}
		else{
			username=user;
			LongwellServer.getServer().setUserName(m_profileID, user);			
		}
	}
	public String getUserName(){
		if(username==null){
			username=LongwellServer.getServer().getUserName(m_profileID);
			
		}
		return username;
			
			
	}
	public Corpus getCorpus(){
		return m_corpus;
	}

	/**
	 * Answer the RDF model from which clients of this profile can query.
	 * However, consider the returned model read-only. That is, data additions
	 * and removals should be done through this profile rather than through
	 * the returned model.
	 *
	 * @return
	 */
	synchronized public Model getModel() {
		return m_inferredModel;
	}

	/**
	 * Answer the first structured model in this profile of the given
	 * implementation type.
	 *
	 * @param modelType
	 * @return
	 */
	synchronized public IStructuredModel getStructuredModel(Class modelType) {
		Iterator i = m_structuredModels.iterator();
		while (i.hasNext()) {
			Object o = i.next();
			if (modelType.isAssignableFrom(o.getClass())) {
				return (IStructuredModel) o;
			}
		}
		return null;
	}

	/**
	 * Answer the first structured model in this profile of the given
	 * implementation type.
	 *
	 * @param modelType
	 * @return
	 */
	synchronized public IStructuredModel getStructuredModel(String modelTypeName) {
		try {
			return getStructuredModel(Class.forName(modelTypeName));
		} catch (ClassNotFoundException e) {
			return null;
		}
	}

	/**
	 * Answer the (first) schema model in this profile.
	 *
	 * @return
	 */
	/*
	synchronized public ISchemaModel getSchemaModel() {
		if (schemaModels.get(m_profileID) == null) {
			m_cachedSchemaModel = (ISchemaModel) getStructuredModel(
				ISchemaModel.class);
			schemaModels.put(m_profileID, (LocalSchemaModel) m_cachedSchemaModel);
		}
		return schemaModels.get(m_profileID);
				//m_cachedSchemaModel;
	}
	*/
	synchronized public ISchemaModel getSchemaModel() {
		if (m_cachedSchemaModel == null) {
			m_cachedSchemaModel = (ISchemaModel) getStructuredModel(
				ISchemaModel.class);
		}
		return m_cachedSchemaModel;
	}
	

	private ISchemaModel m_cachedSchemaModel = null;

	synchronized public boolean containsObject(Resource r) {
		return getModel().contains(r, RDF.type);
	}

	synchronized public void extractObject(Resource r, Model toModel) {
		Model fromModel = getModel();

		LongwellUtilities.copyObject(r, fromModel, toModel);
	}

	/**
	 * Add the given RDF model to the corpus and notify all structured models
	 * of that addition.
	 *
	 * @param model
	 */
	synchronized public void addData(Model model) {
		try {
			
			onBeforeAdd(model);

			m_corpus.add(model);

			onAfterAdd(model);
		} catch (Exception e) {
			/*s_logger.error(
				"Failed to add data to corpus " + m_corpus.getCorpusURI(),
				e);*/

			onFailingAdd(model);
		}
	}

	/**
	 * Add RDF data read from the given file or directory hierarchy and
	 * notify all structured models of that addition.
	 *
	 * @param fileOrDir
	 * @throws IOException
	 */
	synchronized public void addData(File fileOrDir) throws IOException {
		if (fileOrDir.isFile()) {
			LongwellUtilities.loadDataFromFile(fileOrDir, this);
		} else {
			LongwellUtilities.loadDataFromDir(fileOrDir, this, true);
		}
	}
	

	/**
	 * Remove the given RDF model from the corpus and notify all structured
	 * models of that removal.
	 *
	 * @param model
	 */
	synchronized public void removeData(Model model) {
		try {
			onBeforeRemove(model);

			m_corpus.remove(model);

			onAfterRemove(model);
		} catch (Exception e) {
			/*s_logger.error(
				"Failed to remove data from corpus " + m_corpus.getCorpusURI(),
				e);
*/
			onFailingRemove(model);
		}
	}

	/**
	 * Remove all data about the given object.
	 *
	 * @param object
	 */
	synchronized public void removeObject(Resource object) {
		Model model = ModelFactory.createDefaultModel();

		extractObject(object, model);

		removeData(model);
		
		model.close();
	}

	protected void onBeforeAdd(Model model) {
		Iterator i = m_structuredModels.iterator();
		while (i.hasNext()) {
			IStructuredModel sModel = (IStructuredModel) i.next();

			sModel.onBeforeAdd(model);
		}
	}

	protected void onAfterAdd(Model model) {
		Iterator i = m_structuredModels.iterator();
		while (i.hasNext()) {
			IStructuredModel sModel = (IStructuredModel) i.next();

			sModel.onAfterAdd(model);
		}
	}

	protected void onFailingAdd(Model model) {
		Iterator i = m_structuredModels.iterator();
		while (i.hasNext()) {
			IStructuredModel sModel = (IStructuredModel) i.next();

			sModel.onFailingAdd(model);
		}
	}

	protected void onBeforeRemove(Model model) {
		Iterator i = m_structuredModels.iterator();
		while (i.hasNext()) {
			IStructuredModel sModel = (IStructuredModel) i.next();

			sModel.onBeforeRemove(model);
		}
	}

	protected void onAfterRemove(Model model) {
		Iterator i = m_structuredModels.iterator();
		while (i.hasNext()) {
			IStructuredModel sModel = (IStructuredModel) i.next();

			sModel.onAfterRemove(model);
		}
	}

	protected void onFailingRemove(Model model) {
		Iterator i = m_structuredModels.iterator();
		while (i.hasNext()) {
			IStructuredModel sModel = (IStructuredModel) i.next();

			sModel.onFailingRemove(model);
		}
	}

	protected void addStructuredModel(IStructuredModel sModel) {
		m_structuredModels.add(sModel);
	}

	protected void disposeStructuredModels() {
		Iterator i = m_structuredModels.iterator();
		while (i.hasNext()) {
			IStructuredModel model = (IStructuredModel) i.next();

			model.dispose();
		}
		m_structuredModels.clear();

		m_cachedSchemaModel = null;
	}

//	public void addDataFromDB(IDBConnection conn) {
//		
//        Store store = StoreFactory.create("/home/nicola/Scaricati/SDB-1.3.4/oracle-sdb-assembler.ttl") ;
//        Model model = SDBFactory.connectDefaultModel(store) ;
//        this.addData(model);
//
//        store.close() ;
//    
//		
//		
//	}
	
}
